home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
program
/
cpp112.zoo
/
src
/
define.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-06
|
6KB
|
246 lines
/*---------------------------------------------------------------------*\
| |
| CPP -- a stand-alone C preprocessor |
| Copyright (c) 1993 Hacker Ltd. Author: Scott Bigham |
| |
| Permission is granted to anyone to use this software for any purpose |
| on any computer system, and to redistribute it freely, with the |
| following restrictions: |
| - No charge may be made other than reasonable charges for repro- |
| duction. |
| - Modified versions must be clearly marked as such. |
| - The author is not responsible for any harmful consequences of |
| using this software, even if they result from defects therein. |
| |
| define.c -- handle #define and #undef directives |
\*---------------------------------------------------------------------*/
#include <string.h>
#include "global.h"
/*
get_parms() -- return a list of the formal parameters in a macro
definition. Return the number of parameters in |*n|.
*/
static TokenP get_parms(n)
register int *n;
{
Token phead;
register TokenP T, parms = &phead;
*n = 0;
phead.next = NULL;
for (;;) {
T = token();
if (T->type == EOL) {
free_token(T);
goto oops;
}
if (T->type == RPAREN) {
free_token(T);
if (*n == 0)
return NULL;
goto oops;
}
if (T->type != ID) {
free_token(T);
goto oops;
}
parms = parms->next = T;
(*n)++;
T = token();
switch (T->type) {
case COMMA:
free_token(T);
continue;
case RPAREN:
free_token(T);
return phead.next;
default:
free_token(T);
goto oops;
}
}
oops:
error("syntax error in macro definition");
if (parms)
free_tlist(parms);
*n = -1;
return NULL;
}
/*
macro_eq() -- determine if macro bodies |M1| and |M2| are equal, modulo
whitespace. Currently experimental.
*/
int macro_eq(M1, M2)
Macro *M1, *M2;
{
register TokenP h1, h2;
if (M1->flags != M2->flags || M1->nargs != M2->nargs)
return 0;
for (h1 = M1->argnames, h2 = M2->argnames;
h1 && h2;
h1 = h1->next, h2 = h2->next)
if (!streq(token_txt(h1), token_txt(h2)))
return 0;
for (h1 = M1->m_text, h2 = M2->m_text;
h1 && h2;
h1 = h1->next, h2 = h2->next)
if (h1->type != h2->type &&
h1->subtype != h2->subtype &&
h1->hashval != h2->hashval &&
!streq(token_txt(h1), token_txt(h2)) &&
(h1->flags & ~BLUEPAINT) != (h2->flags & ~BLUEPAINT))
return 0;
return 1;
}
/*
is_parm() -- determine if Token |T| is one of the formal parameters of the
macro. Return the "index" into the list of parameters, or -1 if there is
no match
*/
static int is_parm(T, parms)
register TokenP T, parms;
{
int i;
register TokenP t;
if (parms == NULL)
return -1;
for (i = 0, t = parms; t; i++, t = t->next) {
if (streq(token_txt(T), token_txt(t))) {
return i;
}
}
return -1;
}
/* do_define() -- handle a #define directive */
void do_define()
{
TokenP K, L;
register TokenP T, pT;
register Macro *M, *M1;
int i;
static Token head;
_tokenize_line();
K = token();
if (K->type != ID) {
error("argument \"%s\" to #define is not an identifier",
token_txt(K));
free_token(K);
return;
}
M = mk_Macro();
T = token();
if (strlen(token_ws(T)) == 0 && T->type == LPAREN) {
/* a macro with arguments -- get the formal parameters */
free_token(T);
M->flags |= HASARGS;
M->argnames = get_parms(&M->nargs);
if (M->nargs < 0) {
free_Macro(M);
return;
}
} else {
M->nargs = 0;
M->argnames = NULL;
push_tlist(T);
}
L = &head;
head.next = NULL;
for (;;) {
T = token();
if (T->type == EOL) {
free_token(T);
break;
}
switch (T->type) {
case POUND:
free_token(T);
T = token();
if ((i = is_parm(T, M->argnames)) < 0) {
error("# not followed by macro parameter");
push_tlist(T);
break;
}
T->type = MACRO_ARG;
T->flags |= STRINGIZE_ME;
T->val = i;
L = L->next = T;
break;
case TOK_CAT:
free_token(T);
T = token();
if (L == &head || T->type == EOL) {
if (L == &head)
error("## at beginning of macro body");
if (T->type == EOL)
error("## at end of macro body");
break;
}
L->flags |= CONCAT_NEXT;
push_tlist(T);
break;
case ID:
if ((i = is_parm(T, M->argnames)) >= 0) {
T->type = MACRO_ARG;
T->val = i;
}
L = L->next = T;
break;
default:
L = L->next = T;
break;
}
}
/* remove leading space from the resulting sequence */
T = head.next;
if (T)
clear_ws(T);
M->m_text = head.next;
/*
As per the Standard, a macro can only be re-#define'd with an identical
body, modulo whitespace.
*/
if ((M1 = lookup(token_txt(K), K->hashval)) && !macro_eq(M, M1)) {
warning("non-identical redefine of \"%s\"", token_txt(K));
free_Macro(M);
} else {
hash_add(token_txt(K), K->hashval, M);
}
free_token(K);
}
/* do_undefine() -- handle an #undef directive */
void do_undefine()
{
register Macro *M;
register TokenP T;
T = _one_token();
if (T->type != ID) {
if (T->type == EOL)
error("missing argument to #undef");
else
error("argument \"%s\" to #undef is not an identifier",
token_txt(T));
free_token(T);
return;
}
M = lookup(token_txt(T), T->hashval);
if (M && !(M->flags & MAGIC))
hash_remove(token_txt(T), T->hashval);
free_token(T);
T = _one_token();
if (T->type != EOL)
warning("garbage after #undef");
free_token(T);
}